import datetime
import uuid
import taos
import pymysql
import redis
from globals.globals import *
from models import models
from constant.constant import *


conn = taos.connect(host=constant.TAOS_SERVER_HOST, user=constant.TAOS_SERVER_USER,
                         password=constant.TAOS_SERVER_PASSWORD,
                         database=constant.TAOS_SERVER_DATABASE)

pool = redis.ConnectionPool(host=constant.REDIS_HOST, port=constant.REDIS_PORT, password=constant.REDIS_PASSWORD,
                                 decode_responses=True)

alarm_SQL = "SELECT equipmentalarmtag_ai.*, alarmclass.description as alarmclassdesc, alarmgrade.alarm_grade_level, alarmgroup.description as alarmgroupdesc " \
                      f"FROM equipmentalarmtag_ai,alarmclass,alarmgrade,alarmgroup " \
                      f"WHERE equipmentalarmtag_ai.alarm_grade_code = alarmgrade.alarm_grade_code AND " \
                      f"equipmentalarmtag_ai.alarm_group_code = alarmgroup.alarm_group_code AND " \
                      f"equipmentalarmtag_ai.alarm_class_code = alarmclass.alarm_class_code;"


with my_session() as session:
    alarms = session.execute(alarm_SQL).fetchall()

class AiTagAlarm():

    def __init__(self,alarm):
        # 构造模型
        self.alarm_name = alarm.alarm_name
        self.alarm_grade_code = alarm.alarm_grade_code
        self.alarm_group_code = alarm.alarm_group_code
        self.alarm_class_code = alarm.alarm_class_code
        self.alarm_position = alarm.alarm_position
        self.alarmmodeID = alarm.alarmmodeID
        self.taos_equipment_code = alarm.taos_equipment_code
        self.equipmentcode = alarm.equipmentcode
        self.taos_field_name = alarm.taos_field_name
        self.ifconditions = alarm.ifconditions.split(';')
        self.symbol = alarm.symbol.split(';') if alarm.symbol else ''
        self.standvalue = alarm.standvalue.split(';') if alarm.standvalue else ''
        self.llow = int(alarm.llow) if alarm.llow else None
        self.low = int(alarm.low) if alarm.low else None
        self.high = int(alarm.high) if alarm.high else None
        self.hhigh = int(alarm.hhigh) if alarm.hhigh else None
        self.normal = int(alarm.normal) if alarm.normal else None
        self.consistent = int(alarm.consistent) if alarm.consistent else None
        self.alarm_deadzone = alarm.alarm_deadzone
        self.is_stop = alarm.is_stop
        self.description = alarm.description
        self.equipmentID = alarm.equipmentID
        self.readredistable = alarm.readredistable
        self.writeredistable = alarm.writeredistable

        # 连接对象
        self.r = redis.Redis(connection_pool=pool)

        # 辅助参数
        self.value_now = None
        self.value_pre = None
        self.time_out = {'uuid' : '','start_time' : None}
        self.change_rate = {'uuid': '', 'start_time': None}
        self.const_alarm = {'uuid': '', 'start_time': None}
        self.offset_alarm = {'uuid': '', 'start_time': None}
        self.count_value = {'uuid': '', 'start_time': None}
        self.abs_alarm = {
            'uuid_llow': '',
            'uuid_low': '',
            'uuid_high': '',
            'uuid_hhigh': '',
            'start_time_llow': None,
            'start_time_low': None,
            'start_time_high': None,
            'start_time_hhigh': None
        }


    # 生成报警信息
    def alarm_create(self,uuid,mode,start_time):
        with my_session() as session:
            session.add(models.Alarmeventdetail(
            UUID=uuid,
            equipmentID=self.equipmentID,
            equipmentcode=self.equipmentcode,
            alarm_name=self.alarm_name,
            alarm_starttime= str(start_time),
            alarm_endtime= '',
            alarm_duration_time= '',
            description= mode,
            alarm_class_code=self.alarm_class_code,
            alarm_grade_code=self.alarm_grade_code,
            alarm_group_code=self.alarm_group_code,
            alarm_year =  start_time.strftime('%Y'),
            alarm_month = start_time.strftime('%Y-%m'),
            alarm_day =   start_time.strftime('%Y-%m-%d'),
            alarm_hour =  start_time.strftime('%Y-%m-%d %H'),
            alarm_minute =start_time.strftime('%Y-%m-%d %H:%M'),
            alarm_second =start_time.strftime('%Y-%m-%d %H:%M:%S'),
            alarm_status = UNHANDLE
        ))


    # 更新报警信息
    def alarm_update(self,uuid,mode,start_time):
        time_end = datetime.datetime.now()
        alarm_endtime = str(time_end)
        alarm_duration_time = str(int(time_end.timestamp() - start_time.timestamp()))
        SQL = 'update alarmeventdetail set alarm_endtime = "{alarm_endtime}", alarm_duration_time = "{alarm_duration_time}" ' \
              'where UUID = "{uuid}"'.format(alarm_endtime=alarm_endtime, alarm_duration_time=alarm_duration_time,uuid=uuid)
        with my_session() as session:
            session.execute(SQL)
        if mode == TIME_OUT_ALARM:
            self.time_out['uuid'] = ''
        elif mode == CHANGE_RATE_ALARM:
            self.change_rate['uuid'] = ''
        elif mode == CONSIS_ALARM:
            self.const_alarm['uuid'] = ''
        elif mode == LOWER_ALARM:
            self.abs_alarm['uuid_low'] = ''
        elif mode == LLOWER_ALARM:
            self.abs_alarm['uuid_llow'] = ''
        elif mode == HIGHER_ALARM:
            self.abs_alarm['uuid_high'] = ''
        elif mode == HHIGHER_ALARM:
            self.abs_alarm['uuid_hhigh'] = ''
        elif mode == BIAS_ALARM:
            self.offset_alarm['uuid'] = ''
        elif mode == COUNT_ALARM:
            self.count_value['uuid'] = ''

    # 获取当前报警点数据
    def get_value_now(self):
        if len(self.ifconditions) == 1:
            value_now = int(self.r.hget(self.readredistable,self.ifconditions[0])) if self.r.hget(self.readredistable,self.ifconditions[0]) else None
            self.value_now = value_now

    # 超时报警
    def alarm_time_out(self):
        if len(self.ifconditions) == 1:
            SQL = "select stddev({taos_field_name}) from {taos_equipment_code} where ts > now-{out_time} and ts < now"\
                .format(taos_equipment_code = self.taos_equipment_code,taos_field_name = self.taos_field_name, out_time=OUT_TIME)
            try:
                c1 = conn.cursor()
                c1.execute(SQL)
                data = c1.fetchall()
                stddev = data[0][0] if data else 1
                if not stddev:
                    if not self.time_out['uuid']:
                        self.time_out['uuid'] = str(uuid.uuid4())
                        self.time_out['start_time'] = datetime.datetime.now()
                        self.alarm_create(self.time_out['uuid'],TIME_OUT_ALARM, self.time_out['start_time'])
                else:
                    if self.time_out['uuid']:
                        self.alarm_update(self.time_out['uuid'],TIME_OUT_ALARM, self.time_out['start_time'])
                c1.close()
            except Exception as err:
                constant.logger.error(str(err))

    # 变化率报警
    def alarm_change_rate(self):
        if self.value_pre:
            change_rate = abs(self.value_now - self.value_pre) / self.value_pre if self.value_pre else 0
            if change_rate > CHANGE_RATE:
                if not self.time_out['uuid']:
                    self.change_rate['uuid'] = str(uuid.uuid4())
                    self.change_rate['start_time'] = datetime.datetime.now()
                    self.alarm_create(self.change_rate['uuid'], CHANGE_RATE_ALARM, self.change_rate['start_time'])
            else:
                if self.change_rate['uuid']:
                    self.alarm_update(self.change_rate['uuid'], CHANGE_RATE_ALARM, self.change_rate['start_time'])
            self.value_pre = self.value_now
        else:
            self.value_pre = self.value_now


    # 一致性报警
    def alarm_consistent(self):
        if self.consistent:
            if self.value_now != self.consistent:
                if not self.const_alarm['uuid']:
                    self.const_alarm['uuid'] = str(uuid.uuid4())
                    self.const_alarm['start_time'] = datetime.datetime.now()
                    self.alarm_create(self.const_alarm['uuid'], CONSIS_ALARM, self.const_alarm['start_time'])
            else:
                if self.const_alarm['uuid']:
                    self.alarm_update(self.const_alarm['uuid'], CONSIS_ALARM, self.const_alarm['start_time'])


    # 绝对值报警
    def alarm_abs(self):
        if self.hhigh and self.high and self.llow and self.low and self.value_now:
            if self.value_now < self.low and self.value_now >= self.llow:
                if not self.abs_alarm['uuid_low']:
                    self.abs_alarm['uuid_low'] = str(uuid.uuid4())
                    self.abs_alarm['start_time_low'] = datetime.datetime.now()
                    self.alarm_create(self.abs_alarm['uuid_low'], LOWER_ALARM, self.abs_alarm['start_time_low'])
            elif self.value_now < self.llow:
                if not self.abs_alarm['uuid_llow']:
                    self.abs_alarm['uuid_llow'] = str(uuid.uuid4())
                    self.abs_alarm['start_time_llow'] = datetime.datetime.now()
                    self.alarm_create(self.abs_alarm['uuid_llow'], LLOWER_ALARM,self.abs_alarm['start_time_llow'])
            elif self.value_now > self.high and self.value_now <= self.hhigh:
                if not self.abs_alarm['uuid_high']:
                    self.abs_alarm['uuid_high'] = str(uuid.uuid4())
                    self.abs_alarm['start_time_high'] = datetime.datetime.now()
                    self.alarm_create(self.abs_alarm['uuid_high'], HIGHER_ALARM, self.abs_alarm['start_time_high'])
            elif self.value_now > self.hhigh:
                if not self.abs_alarm['uuid_hhigh']:
                    self.abs_alarm['uuid_hhigh'] = str(uuid.uuid4())
                    self.abs_alarm['start_time_hhigh'] = datetime.datetime.now()
                    self.alarm_create(self.abs_alarm['uuid_hhigh'], HHIGHER_ALARM, self.abs_alarm['start_time_hhigh'])
            else:
                if self.abs_alarm['uuid_low']:
                    self.alarm_update(self.abs_alarm['uuid_low'], LOWER_ALARM, self.abs_alarm['start_time_low'])
                elif self.abs_alarm['uuid_llow']:
                    self.alarm_update(self.abs_alarm['uuid_llow'], LLOWER_ALARM, self.abs_alarm['start_time_llow'])
                elif self.abs_alarm['uuid_high']:
                    self.alarm_update(self.abs_alarm['uuid_high'], HIGHER_ALARM, self.abs_alarm['start_time_high'])
                elif self.abs_alarm['uuid_hhigh']:
                    self.alarm_update(self.abs_alarm['uuid_hhigh'], HHIGHER_ALARM, self.abs_alarm['start_time_hhigh'])


    # 偏差值报警
    def alarm_offset(self):
        if len(self.ifconditions) == 1 and self.normal and self.value_now:
            if abs(self.value_now - int(self.normal)) / int(self.normal) > BIAS_RATE:
                if not self.offset_alarm['uuid']:
                    self.offset_alarm['uuid'] = str(uuid.uuid4())
                    self.offset_alarm['start_time'] = datetime.datetime.now()
                    self.alarm_create(self.offset_alarm['uuid'], BIAS_ALARM, self.offset_alarm['start_time'])
            else:
                if self.offset_alarm['uuid']:
                    self.alarm_update(self.offset_alarm['uuid'], BIAS_ALARM, self.offset_alarm['start_time'])


    # 计算值报警
    def alarm_count_value(self):
        if len(self.ifconditions) > 1:
            expression = list(zip(self.ifconditions, self.symbol, self.standvalue))
            result_count = 0
            for exp in expression:
                ifcon = self.r.hget('data_realtime', exp[0])
                standvalue = exp[2]
                result = operators[exp[1]](ifcon, standvalue) if ifcon else None
                result_count += 1 if result else 0

            if len(expression) == result_count:
                if not self.count_value['uuid']:
                    self.count_value['uuid'] = str(uuid.uuid4())
                    self.count_value['start_time'] = datetime.datetime.now()
                    self.alarm_create(self.count_value['uuid'], COUNT_ALARM, self.count_value['start_time'])
            else:
                if self.count_value['uuid']:
                    self.alarm_update(self.count_value['uuid'], COUNT_ALARM, self.count_value['start_time'])

    # 报警方法组
    def _alarms(self):
        try:
            # 获取当前值
            self.get_value_now()
            # 超时报警
            self.alarm_time_out()
            # 变化率报警
            self.alarm_change_rate()
            # 一致性报警
            self.alarm_consistent()
            # 绝对值报警
            self.alarm_abs()
            # 超限报警
            self.alarm_offset()
            # 计算值报警
            self.alarm_count_value()
        except Exception as err:
            constant.logger.error(str(err))


alarm_nodels = list()
def get_ai_alarm_model():
    '''
    构造报警模型列表
    '''
    for alarm in alarms:
        alarm_nodels.append(AiTagAlarm(alarm))

get_ai_alarm_model()


def alarm_job():
    '''
    各报警模型执行报警方法组
    '''
    for model in alarm_nodels:
        model._alarms()



